//文件名: sip/sip.c
//
//描述: 这个文件实现SIP进程  

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <signal.h>
#include <netdb.h>
#include <assert.h>
#include <sys/utsname.h>
#include <pthread.h>
#include <unistd.h>

#include "../common/constants.h"
#include "../common/pkt.h"
#include "../common/seg.h"
#include "../topology/topology.h"
#include "../utils/concurrent.h"
#include "sip.h"
#include "nbrcosttable.h"
#include "dvtable.h"
#include "routingtable.h"
#include "../common/common.h"

//SIP层等待这段时间让SIP路由协议建立路由路径. 
#define SIP_WAITTIME 60

/**************************************************************/
//声明全局变量
/**************************************************************/
int son_conn; 			//到重叠网络的连接
int stcp_conn;			//到STCP的连接
nbr_cost_entry_t* nct;			//邻居代价表
dv_t* dvt;				//距离矢量表
pthread_mutex_t* dvt_mutex;		//距离矢量表互斥量
routingtable_t* routingtable;		//路由表
pthread_mutex_t* routingtable_mutex;	//路由表互斥量
/**************************************************************/
//实现SIP的函数
/**************************************************************/

//SIP进程使用这个函数连接到本地SON进程的端口SON_PORT.
//成功时返回连接描述符, 否则返回-1.
int connectToSON() 
{ 
	int node_ip=topology_getMyNodeIP();
	int sockfd=socket(PF_INET,SOCK_STREAM,0);
	struct sockaddr_in son_sockaddr;
	son_sockaddr.sin_family=AF_INET;
	son_sockaddr.sin_addr.s_addr=node_ip;
	son_sockaddr.sin_port=htons(SON_PORT);
	if(connect(sockfd,(struct sockaddr*)&son_sockaddr,sizeof(son_sockaddr))==0){
		return sockfd;
	}else{
		return -1;
	}
}

//这个线程每隔ROUTEUPDATE_INTERVAL时间发送路由更新报文.路由更新报文包含这个节点
//的距离矢量.广播是通过设置SIP报文头中的dest_nodeID为BROADCAST_NODEID,并通过son_sendpkt()发送报文来完成的.
void* routeupdate_daemon(void* arg) 
{
	while(1)
	{
		sip_pkt_t pkt;
		pkt.header.src_nodeID=topology_getMyNodeID();
		pkt.header.dest_nodeID=BROADCAST_NODEID;
		pkt.header.type=ROUTE_UPDATE;
		pkt.header.length=sizeof(pkt_routeupdate_t);
		pkt_routeupdate_t dv;
		dv.entryNum=topology_getNodeNum();
		pthread_mutex_lock(dvt_mutex);
		for(int i=0;i<dv.entryNum;i++)
		{
			dv.entry[i].nodeID=dvt[0].dvEntry[i].nodeID;
			dv.entry[i].cost=dvt[0].dvEntry[i].cost;
		}
		pthread_mutex_unlock(dvt_mutex);

		memcpy((void*)pkt.data,(void*)&dv,sizeof(pkt_routeupdate_t));

		int ret = son_sendpkt(BROADCAST_NODEID,&pkt,son_conn);

		if(ret < 0)
		{
			concurrent_printf_warning("routeupdate_daemon:son_sendpkt failed!\n");
			break;
		}
		sleep(ROUTEUPDATE_INTERVAL);
	}
	concurrent_printf_reminder("routeupdate_daemon thread exit!\n");
	pthread_exit(NULL);
}


void handle_sippkt(sip_pkt_t* pkt)
{
	int self_nodeID = topology_getMyNodeID();

	if(pkt->header.dest_nodeID == self_nodeID)
	{	
		seg_t seg;memset(&seg,0,sizeof(seg));
		memcpy(&seg,pkt->data,pkt->header.length);
		if(stcp_conn>0 && forwardsegToSTCP(stcp_conn,pkt->header.src_nodeID,&seg) < 0 )
			concurrent_printf_warning("handle_sippkt:forwardsegToSTCP failed!\n");
	}
	else//路由
	{
		pthread_mutex_lock(routingtable_mutex);
		int next_nodeID = routingtable_getnextnode(routingtable,pkt->header.dest_nodeID);
		pthread_mutex_unlock(routingtable_mutex);

		int ret = son_sendpkt(next_nodeID,pkt,son_conn);
		if(ret < 0)concurrent_printf_warning("handle_sippkt:sendpkt failed!\n");
	}
}

void update_DistanceVrctor_and_RoutingTable()
{
	int node_num=topology_getNodeNum();
	int nbr_num=topology_getNbrNum();
	int self_nodeID=dvt[0].nodeID;

	for(int i=0;i<node_num;i++)//遍历更新自己的distance vector
	{	
		int dest_nodeID=dvt[0].dvEntry[i].nodeID;
		int new_cost=nbrcosttable_getcost(nct,dest_nodeID);
		int next_nodeID=dest_nodeID;

		for(int j=1;j<nbr_num;j++)//遍历所有邻居的相应值以计算最小值
		{
			int nbr_nodeID=dvt[j].nodeID;
			int nbr_cost = nbrcosttable_getcost(nct,nbr_nodeID);
			int updated_cost = nbr_cost + dvtable_getcost(dvt,nbr_nodeID,dest_nodeID);
			if(updated_cost < new_cost)
			{
				new_cost = updated_cost;
				next_nodeID = nbr_nodeID;
			}
		}
		
		pthread_mutex_lock(dvt_mutex);
		dvtable_setcost(dvt,self_nodeID,dest_nodeID,new_cost);
		pthread_mutex_unlock(dvt_mutex);

		pthread_mutex_lock(routingtable_mutex);
		routingtable_setnextnode(routingtable,dest_nodeID,next_nodeID);
		pthread_mutex_unlock(routingtable_mutex);
	}
}

void handle_routeupdatepkt(sip_pkt_t* pkt)
{
	pkt_routeupdate_t dv;
	memcpy(&dv,pkt->data,sizeof(pkt_routeupdate_t));

	int src_id=pkt->header.src_nodeID;
	
	pthread_mutex_lock(dvt_mutex);
	for(int i=0;i<dv.entryNum;i++)
	{
		dvtable_setcost(dvt,src_id,dv.entry[i].nodeID,dv.entry[i].cost);
	}
	pthread_mutex_unlock(dvt_mutex);

	update_DistanceVrctor_and_RoutingTable();

	nbrcosttable_print(nct);
	dvtable_print(dvt);
	routingtable_print(routingtable);
}

//这个线程处理来自SON进程的进入报文. 它通过调用son_recvpkt()接收来自SON进程的报文.
//如果报文是SIP报文,并且目的节点就是本节点,就转发报文给STCP进程. 如果目的节点不是本节点,
//就根据路由表转发报文给下一跳.如果报文是路由更新报文,就更新距离矢量表和路由表.
void* pkthandler(void* arg) 
{
	sip_pkt_t pkt;
	while(son_recvpkt(&pkt,son_conn)==1) 
	{
		switch(pkt.header.type){
			case SIP:{
				handle_sippkt(&pkt);
				break;
			}
			case ROUTE_UPDATE:{
				handle_routeupdatepkt(&pkt);
				break;
			}
			default:{
				concurrent_printf_warning("pkthandler:unknown pkt type!\n");
				break;
			}
		}
	}
	close(son_conn);
	pthread_exit(NULL);
}

//这个函数终止SIP进程, 当SIP进程收到信号SIGINT时会调用这个函数. 
//它关闭所有连接, 释放所有动态分配的内存.
void sip_stop() {
	close(son_conn);
	exit(1);
}



//这个函数打开端口SIP_PORT并等待来自本地STCP进程的TCP连接.
//在连接建立后, 这个函数从STCP进程处持续接收包含段及其目的节点ID的sendseg_arg_t. 
//接收的段被封装进数据报(一个段在一个数据报中), 然后使用son_sendpkt发送该报文到下一跳. 下一跳节点ID提取自路由表.
//当本地STCP进程断开连接时, 这个函数等待下一个STCP进程的连接.
void waitSTCP() 
{
	struct sockaddr_in sockaddr=create_sockaddr(topology_getMyNodeIP(),SIP_PORT);
	socklen_t addr_len=sizeof(sockaddr);

	int server_socketfd=socket(PF_INET,SOCK_STREAM,0);
	bind(server_socketfd,(struct sockaddr*)&sockaddr,addr_len);
	listen(server_socketfd,128);
	stcp_conn=accept(server_socketfd,(struct sockaddr*)&sockaddr,&addr_len);
	close(server_socketfd);

	if(stcp_conn<0)
	{
		concurrent_printf_warning("waitSTCP:STCP accept failed!\n");
		return ;
	}
	else
		concurrent_printf_reminder("STCP CONNECTED!\n");
	
	while(1)
	{	
		int dest_nodeID;
		seg_t seg;
		if(getsegToSend(stcp_conn,&dest_nodeID,&seg) < 0)
		{
			concurrent_printf_warning("waitSTCP:getsegToSend failed!\n");
			break;
		}

		sip_pkt_t pkt;
		pkt.header.src_nodeID=topology_getMyNodeID();
		pkt.header.dest_nodeID=dest_nodeID;
		pkt.header.type=SIP;
		pkt.header.length=sizeof(stcp_hdr_t)+seg.header.length;
		memcpy(pkt.data,&seg,sizeof(seg));

		pthread_mutex_lock(routingtable_mutex);
		int next_nodeID=routingtable_getnextnode(routingtable,dest_nodeID);
		pthread_mutex_unlock(routingtable_mutex);

		int ret = son_sendpkt(next_nodeID,&pkt,son_conn);

		if(ret < 0)concurrent_printf("waitSTCP:son_sendpkt failed!\n");
	}
}

int main(int argc, char *argv[]) {
	printf("SIP layer is starting, pls wait...\n");

	//初始化全局变量
	nct = nbrcosttable_create();
	dvt = dvtable_create();
	dvt_mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
	pthread_mutex_init(dvt_mutex,NULL);
	routingtable = routingtable_create();
	routingtable_mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
	pthread_mutex_init(routingtable_mutex,NULL);
	son_conn = -1;
	stcp_conn = -1;

	nbrcosttable_print(nct);
	dvtable_print(dvt);
	routingtable_print(routingtable);

	//注册用于终止进程的信号句柄
	signal(SIGINT, sip_stop);

	//连接到本地SON进程 
	son_conn = connectToSON();
	if(son_conn<0) {
		printf("can't connect to SON process\n");
		exit(1);		
	}
	
	//启动线程处理来自SON进程的进入报文 
	pthread_t pkt_handler_thread; 
	pthread_create(&pkt_handler_thread,NULL,pkthandler,(void*)0);

	//启动路由更新线程 
	pthread_t routeupdate_thread;
	pthread_create(&routeupdate_thread,NULL,routeupdate_daemon,(void*)0);	

	printf("SIP layer is started...\n");
	printf("waiting for routes to be established\n");
	//sleep(SIP_WAITTIME);
	//sleep(10);
	routingtable_print(routingtable);

	//等待来自STCP进程的连接
	printf("waiting for connection from STCP process\n");
	waitSTCP(); 
}